import Link from "next/link";
import { BillingAutoRefresh, BillingRefreshListener } from "@/components/billing-refresh-listener";
import { BillingPageState } from "@/components/billing-page-state";
import {
type BillingCredits,
type BillingInvoices,
type BillingSubscription,
PopiartApiError,
getBillingCredits,
getBillingInvoices,
getBillingSubscription,
getViewerSession,
} from "@/lib/popiart-api";
import { type Locale } from "@/lib/site-content";
export const dynamic = "force-dynamic";
function formatNumber(locale: Locale, value: number) {
return new Intl.NumberFormat(locale === "zh" ? "zh-CN" : "en-US").format(value);
}
function formatError(error: unknown) {
if (error instanceof PopiartApiError) {
return error.message;
}
if (error instanceof Error) {
return error.message;
}
return String(error);
}
export default async function BillingPage({
params,
searchParams,
}: {
params: Promise<{ locale: string }>;
searchParams: Promise<{ kind?: string; status?: string; page?: string }>;
}) {
const { locale } = await params;
const { kind, status, page } = await searchParams;
const typedLocale = locale as Locale;
const isZh = typedLocale === "zh";
const session = await getViewerSession();
const title = isZh ? "账单中心" : "Billing";
const subtitle = isZh
? "查看当前订阅、积分钱包和订单历史。"
: "Review your active subscription, point wallets, and order history.";
const loginCta = isZh ? "去登录" : "Sign in";
const consoleCta = isZh ? "前往控制台" : "Open console";
const unboundTitle = isZh ? "先绑定网关用户" : "Bind a gateway user first";
const unboundBody = isZh
? "当前 session 还没有绑定网关用户态,先去控制台绑定后才能读取真实账单与订单。"
: "This session is not bound to a gateway user yet. Bind it in the console first to read live billing and order data.";
const subscriptionTitle = isZh ? "当前订阅" : "Current subscription";
const creditsTitle = isZh ? "积分钱包" : "Point wallets";
const invoicesTitle = isZh ? "订单历史" : "Order history";
const noSubscription = isZh ? "当前没有有效订阅。" : "No active subscription.";
const noCredits = isZh ? "当前没有积分钱包记录。" : "No point wallets.";
const noOrders = isZh ? "当前没有订单记录。" : "No orders yet.";
const kindFilter = kind === "subscription" || kind === "points" ? kind : "all";
const statusFilter = typeof status === "string" && status.trim() ? status.trim() : "all";
const currentPage = Number.isFinite(Number(page)) && Number(page) > 0 ? Number(page) : 1;
const pageSize = 8;
const kindLabel = isZh ? "类型" : "Kind";
const statusLabel = isZh ? "状态" : "Status";
const allLabel = isZh ? "全部" : "All";
const subscriptionsLabel = isZh ? "订阅订单" : "Subscription orders";
const pointsLabel = isZh ? "积分包订单" : "Point orders";
const prevLabel = isZh ? "上一页" : "Previous";
const nextLabel = isZh ? "下一页" : "Next";
const pageLabel = isZh ? "页码" : "Page";
if (!session) {
return (
{isZh ? "登录后查看真实账单" : "Sign in to view live billing"}
{loginCta}
);
}
if (!session.gateway_bound) {
return (
{unboundTitle}
{unboundBody}
{consoleCta}
);
}
let subscription: BillingSubscription | null = null;
let credits: BillingCredits | null = null;
let invoices: BillingInvoices | null = null;
const errors: string[] = [];
const [subscriptionResult, creditsResult, invoicesResult] = await Promise.allSettled([
getBillingSubscription(),
getBillingCredits(),
getBillingInvoices(),
]);
if (subscriptionResult.status === "fulfilled") {
subscription = subscriptionResult.value;
} else {
errors.push(formatError(subscriptionResult.reason));
}
if (creditsResult.status === "fulfilled") {
credits = creditsResult.value;
} else {
errors.push(formatError(creditsResult.reason));
}
if (invoicesResult.status === "fulfilled") {
invoices = invoicesResult.value;
} else {
errors.push(formatError(invoicesResult.reason));
}
type BillingOrderItem = Record & { __kind: "subscription" | "points" };
const allOrders: BillingOrderItem[] = [
...((invoices?.subscription_orders?.items || []).map((item) => ({ ...item, __kind: "subscription" as const }))),
...((invoices?.point_orders?.items || []).map((item) => ({ ...item, __kind: "points" as const }))),
];
const filteredOrders = allOrders.filter((item) => {
if (kindFilter !== "all" && item.__kind !== kindFilter) {
return false;
}
if (statusFilter !== "all" && String(item.status || "") !== statusFilter) {
return false;
}
return true;
});
const totalPages = Math.max(1, Math.ceil(filteredOrders.length / pageSize));
const safePage = Math.min(currentPage, totalPages);
const pagedOrders = filteredOrders.slice((safePage - 1) * pageSize, safePage * pageSize);
const availableStatuses = Array.from(
new Set(
allOrders
.map((item) => String(item.status || "").trim())
.filter(Boolean),
),
);
const hasPendingOrders = allOrders.some((item) => {
const currentStatus = String(item.status || "").trim();
return currentStatus !== "" && !["success", "SUCCESS", "TRADE_SUCCESS", "failed", "FAILED", "closed", "CLOSED", "TRADE_CLOSED"].includes(currentStatus);
});
function buildBillingHref(nextKind: string, nextStatus: string, nextPage: number) {
const query = new URLSearchParams();
if (nextKind !== "all") {
query.set("kind", nextKind);
}
if (nextStatus !== "all") {
query.set("status", nextStatus);
}
if (nextPage > 1) {
query.set("page", String(nextPage));
}
const serialized = query.toString();
return serialized ? `/${locale}/billing?${serialized}` : `/${locale}/billing`;
}
return (
{errors.length > 0 ? (
{errors.join(" | ")}
) : null}
{subscriptionTitle}
{subscription && subscription.subscriptions.length > 0 ? (
{subscription.subscriptions.map((item, index) => (
{item.subscription?.member_level || "subscription"}
{item.subscription?.status || "-"}
{isZh ? "可用积分" : "Available points"}
{formatNumber(
typedLocale,
subscription.subscription_points[String(item.subscription?.id)]?.available_points || 0,
)}
))}
) : (
{noSubscription}
)}
{creditsTitle}
{credits && credits.wallets.length > 0 ? (
{isZh ? "当前余额" : "Balance"}
{formatNumber(typedLocale, credits.balance)}
{isZh ? "钱包数量" : "Wallet count"}
{formatNumber(typedLocale, credits.wallets.length)}
{credits.wallets.map((wallet) => (
{wallet.source_type}
{isZh ? "可用积分" : "Available points"}: {formatNumber(typedLocale, wallet.points)}
{isZh ? "总积分" : "Total points"}
{formatNumber(typedLocale, wallet.points_total)}
))}
) : (
{noCredits}
)}
{invoicesTitle}
{allOrders.length > 0 ? (
{kindLabel}
{allLabel}
{subscriptionsLabel}
{pointsLabel}
{statusLabel}
{allLabel}
{availableStatuses.map((itemStatus) => (
{itemStatus}
))}
{pagedOrders.map((item, index) => (
{String(item.plan_title || item.package_name || item.trade_no || "order")}
{String(item.status || "-")}
{String(item.money || "-")} {String(item.currency || "")}
{String(item.payment_method || "-")}
{isZh ? "交易号" : "Trade no"}
{String(item.trade_no || "-")}
{isZh ? "退款状态" : "Refund status"}
{String(item.refund_status || "-")}
{isZh ? "完成时间" : "Completed at"}
{String(item.complete_time || "-")}
{item.__kind === "subscription" ? (isZh ? "订阅 ID" : "Subscription ID") : (isZh ? "到账积分" : "Delivered points")}
{String(item.__kind === "subscription" ? item.subscription_id : item.points_amount || "-")}
))}
{prevLabel}
{pageLabel}: {safePage} / {totalPages}
= totalPages}
className={`button button-light button-small ${safePage >= totalPages ? "button-disabled" : ""}`}
href={buildBillingHref(kindFilter, statusFilter, Math.min(totalPages, safePage + 1))}
>
{nextLabel}
) : (
{noOrders}
)}
);
}